import os
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

# 读取数据
def load_iris():
    
    iris = pd.read_csv("../Datasets/iris/Iris.csv")
    # print(iris.head())
    # print(iris["Species"].value_counts())
    #sns初始化
    # sns.set()
    #设置散点图x轴与y轴以及data参数
    # sns.relplot(x='SepalLengthCm', y='SepalWidthCm', hue='Species', style='Species', data=iris )
    # plt.title('SepalLengthCm and SepalWidthCm data analysize')
    # plt.show()
    # 取前一百行的前两列作为感知机的特征，最后一列作为输出
    data = np.array(iris.iloc[:100, [1, 2, -1]])
    x, y = data[:,:-1], data[:,-1]
    y = np.array([1 if i == 'Iris-setosa' else -1 for i in y])

    return x,y

# 模型定义
class Perceptron:

    # 原始形式算法
    # w_dims 表示特征个数
    def __init__(self,w_dims,rate=0.1): # 初始化

        self.w = np.zeros(w_dims)
        self.b = 0
        self.rate = rate

    def sign(self,x): # 前向传播

        pred = np.sign( np.matmul(self.w, x) + self.b )

        return pred

    def fit(self,x,y): # 训练
        
        AllPass = False
        while AllPass is not True:

            AllPass = True
            for i in range(x.shape[0]):

                flag = y[i] * ( np.matmul(self.w, x[i]) + self.b )
                if flag <= 0:
                    self.w = self.w + self.rate * y[i] * x[i]
                    self.b = self.b + self.rate * y[i]
                    AllPass = False
        
        print('Mission Complete!')

    def score(self): # 测试
        pass

class Perceptron_Duality:

    # 对偶形式算法
    # x_nums表示训练集中元素的个数
    def __init__(self,x_nums,rate=0.1): # 初始化

        self.a = np.zeros(x_nums)
        self.b = 0
        self.rate = rate
        # w 要在训练结束后更新，不然无法在没有y的情况下前向传播
        self.w = 0

    def sign(self,x): # 前向传播

        pred = np.sign( np.matmul(self.w, x) + self.b )

        return pred

    def fit(self,x,y): # 训练
        
        # 构建Gram内积矩阵
        # Gram = np.matmul( x, np.transpose(x) )
        # np.save('Gram_iris.npy',Gram)
        Gram = np.load('Gram_iris.npy',allow_pickle=True)

        AllPass = False
        while AllPass is not True:

            AllPass = True
            for i in range(x.shape[0]):

                flag = y[i] * ( np.dot( self.a, y*Gram[:,i] ) + self.b )
                if flag <= 0:
                    self.a[i] = self.a[i] + self.rate 
                    self.b = self.b + self.rate * y[i]
                    AllPass = False
        
        self.w = np.matmul( self.a*y, x )
        print('Mission Complete!')

    def score(self): # 测试
        pass

# 主函数
def main():

    # 加载数据，打印基本信息
    x,y = load_iris()
    print('x Shape:{:<25s} y Shape:{:<25s}'.format(str(x.shape),str(y.shape)))
    print('x type :{:<25s} y type :{:<25s}'.format(str(type(x)),str(type(y))))

    # 创建模型与训练
    # model = Perceptron(x.shape[1])
    model = Perceptron_Duality(x.shape[0])
    model.fit(x,y)
    print(model.w,model.b)

    # 绘制学习到的直线
    x_points = np.linspace(4, 7, 10)
    y_ = -(model.w[0] * x_points + model.b) / model.w[1]
    plt.plot(x_points, y_)

    # 绘制数据散点图
    plt.plot(x[:50, 0], x[:50, 1], 'bo', color='blue', label='0')
    plt.plot(x[50:100, 0], x[50:100, 1], 'bo', color='orange', label='1')
    plt.xlabel('sepal length')
    plt.ylabel('sepal width')
    plt.grid(ls=":",c='b',)
    plt.show()

if __name__ == '__main__':
    main()